﻿using System;
using System.Collections.Generic;

namespace AIS2
{
	public class ValueLine<Tvalue, Tsource>
	{
		public ValueLine()
		{
			SetErrorManager(null);
			Reset();
		}

		public Tvalue Value { get; private set; }

		/// <summary>
		/// Значение валидно
		/// </summary>
		public bool IsValid { get; private set; }

		/// <summary>
		/// Функция 
		/// </summary>
		public Func<Tsource, Tvalue> GetterFunc { get; private set; }

		public ICollection<Error> ErrorManager { get; private set; }

		public ValueLine<Tvalue, Tsource> SetErrorManager(ICollection<Error> errs)
		{
			ErrorManager = errs ?? new List<Error>();
			return this;
		}

		public ValueLine<Tvalue, Tsource> Reset()
		{
			Value = default(Tvalue);
			//GetterFunc = s => default(Tvalue);
			IsValid = true;
			//VerifyErrors.Clear();
			return this;
		}

		public ValueLine<Tvalue, Tsource> GetFrom(Tsource xe)
		{
			Value = GetterFunc(xe);
			return this;
		}

		public ValueLine<Tvalue, Tsource> SetValue(Tvalue value)
		{
			Value = value;
			return this;
		}

		/// <summary>
		/// Предварительная обработка значения
		/// </summary>
		/// <param name="presetFunc"></param>
		/// <returns></returns>
		public ValueLine<Tvalue, Tsource> Edit(Func<Tvalue, Tvalue> presetFunc)
		{
			Value = presetFunc(Value);
			return this;
		}

		/// <summary>
		/// Установка функции получения значения
		/// </summary>
		/// <param name="func">Функция получения значения из источника</param>
		/// <returns></returns>
		public ValueLine<Tvalue, Tsource> Getter(Func<Tsource, Tvalue> func)
		{
			GetterFunc = func ?? (s => default(Tvalue));
			return this;
		}

		/// <summary>
		/// Валидация значения
		/// </summary>
		/// <param name="func">Функция проверки</param>
		/// <param name="descError">Описание ошибки в случае неуспешной проверки</param>
		/// <param name="level">Уровень ошибки. Если меньше ErrorLevel.Error, то значение считается валидным</param>		
		/// <returns></returns>
		public ValueLine<Tvalue, Tsource> Is(Func<Tvalue, bool> func, string descError, ErrorLevel level)
		{
			if (func != null && !func(Value))
			{
				IsValid = IsValid && level == ErrorLevel.Valid;
				ErrorManager.Add(Error.Get(descError, level));
			}
			return this;
		}

	    /// <summary>
		/// Валидация значения
	    /// </summary>
	    /// <typeparam name="Tclass">Подкласс ошибки/контекста</typeparam>
		/// <param name="func">Функция проверки</param>
		/// <param name="descError">Описание ошибки в случае неуспешной проверки</param>
		/// <param name="level">Уровень ошибки. Если меньше ErrorLevel.Error, то значение считается валидным</param>
	    /// <param name="subject">Субъект ошибки</param>
	    /// <returns></returns>
		public ValueLine<Tvalue, Tsource> Is<Tclass>(Func<Tvalue, bool> func, string descError, ErrorLevel level, Tclass subject)
		{
			if (func != null && !func(Value))
			{
				IsValid = IsValid && level == ErrorLevel.Valid;
				ErrorManager.Add(Error<Tclass>.Get(descError, level, subject));
			}
			return this;
		}

		/// <summary>
		/// Валидация значения
		/// </summary>
		/// <param name="func">Функция проверки</param>
		/// <param name="level">Уровень ошибки. Если меньше ErrorLevel.Error, то значение считается валидным</param>
		/// <returns></returns>
		public ValueLine<Tvalue, Tsource> Is(Func<Tvalue, bool> func, ErrorLevel level)
		{
			if (func != null && !func(Value))
				IsValid = IsValid && level == ErrorLevel.Valid;
			return this;
		}

		/// <summary>
		/// Если значение прошло валидацию, то будет присвоено значение
		/// </summary>
		/// <param name="assign">Процедура присваивания</param>
		/// <returns></returns>
		public ValueLine<Tvalue, Tsource> Assign(Action<Tvalue> assign)
		{
			if (IsValid)
				assign(Value);
			return this;
		}

		/// <summary>
		/// Безусловное присвоение
		/// </summary>
		/// <param name="assign">Процедура присваивания</param>
		/// <returns></returns>
		public ValueLine<Tvalue, Tsource> AssignInAnyCase(Action<Tvalue> assign)
		{
			assign(Value);
			return this;
		}

		/// <summary>
		/// Если значение прошло валидацию, то будет присвоено значение
		/// </summary>
		/// <param name="assign">Процедура присваивания</param>
		/// <param name="assignForNotValid">Процедура присвоения для невалидного значения</param>
		/// <returns></returns>
		public ValueLine<Tvalue, Tsource> Assign(Action<Tvalue> assign, Action<Tvalue> assignForNotValid)
		{
			if (IsValid)
				assign(Value);
			else
				assignForNotValid(Value);
			return this;
		}
	}
}